#!/usr/local/bin/python
################################################################################
# common.py
#
# UTF8SMTP NIDAMail Program
# Version 1.0
# Date : July 23, 2008
# Homepage: http://sourceforge.net/projects/utf8smtp/
################################################################################

# Reload configuration (NIDAMail.conf) every 86400 seconds (= 24 hours)
################################################################################
LOAD_INTERVAL = 86400
################################################################################
PROGRAM_NAME = 'NIDAMail'
PROGRAM_MAIL = 'eai-devel@googlegroups.com'
PROGRAM_HOMEPAGE = 'http://sourceforge.net/projects/utf8smtp/'
PROGRAM_VERSION = 'UTF8SMTP NIDAMail 1.0 (July 23, 2008)'

conf = {'BASE_PATH':'/%s' % (PROGRAM_NAME),
		'SERVER_NAME':'localhost',
		'RETURN_ID':'postmaster',
		'USER':'nobody',
		'GROUP':'nobody',
		'IDN_USE':0,
		'IDN_CHARSET':'UTF-8',
		'ESMTPD_TYPE':'threading',
		'ESMTPD_PORT':25,
		'ESMTPD_TIMEOUT':10*60,
		'ESMTPD_MAX_THREADS':512,
		'POP3D_TYPE':'threading',
		'POP3D_PORT':110,
		'POP3D_TIMEOUT':10*60,
		'POP3D_MAX_THREADS':512,
		'SENDER_TYPE':'threading',
		'SENDER_PORT':25,
		'SENDER_TIMEOUT':10*60,
		'SENDER_MAX_THREADS':100,
		'SENDER_RETRY_COUNT':0, 
		'SENDER_RETRY_INTERVAL':10*60,
		'DECODER_TYPE':'alone',
		'DECODER_MAX_THREADS':10,
		'ENCODER_TYPE':'alone',
		'ENCODER_MAX_THREADS':10,
		'CHARSET':'UTF-8',
		'LOG_DIR':'/%s_Data/LOG' % (PROGRAM_NAME),
		'RECV_QUEUE':'/%s_Data/TEMP' % (PROGRAM_NAME),
		'SEND_QUEUE':'/%s_Data/SEND' % (PROGRAM_NAME),
		'DECODE_QUEUE':'/%s_Data/DECODING' % (PROGRAM_NAME),
		'ENCODE_QUEUE':'/%s_Data/ENCODING' % (PROGRAM_NAME)}

################################################################################
# Create an empty dictionary for vh
vh = {}

# Create an array for access
access = []

# Create a dictionary for lmtime (key:value)
lmtime = {'CONF':[0, 0], 'VH':[0, 0], 'ACCESS':0}
################################################################################
import datetime, time, os, stat, re, urllib, shutil, decimal, hashlib, sys, traceback, pwd, grp, crypt, sqlite
import _multibytecodec, _strptime
from encodings import ascii, utf_8, utf_16, euc_kr, cp949, iso2022_kr, big5, gb2312, gb18030, gbk, hz, euc_jp, shift_jis, iso2022_jp, koi8_r, koi8_u, latin_1, hex_codec, punycode, idna
from email import base64MIME
from email import header

################################################################################
def deleteFile(szFILEPATH, szPROCESS):
	try: os.unlink(szFILEPATH)
	except: printError("%-7s | Can't unlink %s" % (szPROCESS, szFILEPATH), szPROCESS)

################################################################################
def renameFile(szSRCPATH, szDESTPATH, szPROCESS):
	try: os.rename(szSRCPATH, szDESTPATH)
	except: printError("%-7s | Can't rename %s to %s" % (szPROCESS, szSRCPATH, szDESTPATH), szPROCESS)

################################################################################
def removeAttachedHeader(szFILEPATH, szPROCESS):
	try:
		f = open(szFILEPATH, 'r')
		f2 = open('%s__TEMP__' % szFILEPATH, 'w')
		for lineStr in f:
			if lineStr.strip() == 'DATA': break
		for lineStr in f: f2.write(lineStr)
		f2.close()
		f.close()
		os.unlink(szFILEPATH)
		os.rename('%s__TEMP__' % szFILEPATH, szFILEPATH)
	except: printError("%-7s | Can't removeAttachedHeader %s" % (szPROCESS, szFILEPATH), szPROCESS)

################################################################################
def convertCHARSET(incharset):
	szInCharset = incharset.strip().lower().replace('-', '').replace('_', '').replace(' ', '')
	if szInCharset == 'utf8': incharset = 'utf-8'
	elif szInCharset in ('euckr', 'cp949', 'ksc5601', 'ksc56011987'): incharset = 'cp949'
	elif szInCharset.startswith('iso2022kr'): incharset = 'iso2022_kr'
	elif szInCharset in ('eucjp', 'shiftjis', 'cp932', 'jis'): incharset = 'euc_jp'
	elif szInCharset.startswith('iso2022jp'): incharset = 'iso2022_jp'
	elif szInCharset.startswith('windows'): incharset = 'latin'
	elif szInCharset.startswith('iso8859'): incharset = 'latin'
	elif szInCharset == 'gb18030': incharset = 'gb18030'
	elif szInCharset == 'gb2312': incharset = 'gb2312'
	elif szInCharset == 'gbk': incharset = 'gbk'
	elif szInCharset == 'hz': incharset = 'hz'
	elif szInCharset == 'big5': incharset = 'big5'
	elif szInCharset == 'koi8r': incharset = 'koi8_r'
	elif szInCharset == 'koi8u': incharset = 'koi8_u'
	return incharset

################################################################################
def encodeCHARSET(str, incharset, outcharset):
	incharset = convertCHARSET(incharset)
	outcharset = convertCHARSET(outcharset)
	try:
		if incharset != outcharset: str = unicode(str, incharset, 'ignore').encode(outcharset, 'ignore')
		retStr = '=?%s?B?%s?=' % (outcharset, base64MIME.encode(str , True, 76, ''))
	except:
		printError("common  | Can't encodeCHARSET(%s, %s, %s)" % (incharset, outcharset, str), 'common')
		return str
	else: return retStr

################################################################################
def decodeCHARSET(str, incharset, outcharset, szPROCESS, key19):
	incharset = convertCHARSET(incharset)
	outcharset = convertCHARSET(outcharset)
	retStr = ''
	try:
		tkns = re.split('(=\?.*\?.*\?.*\?=)', str)
		for strtkn in tkns:
			if strtkn == '': continue
			tkn = header.decode_header(strtkn)[0]
			if tkn[1]: incharset = tkn[1]
			incharset = convertCHARSET(incharset)
			if incharset == outcharset: retStr += tkn[0]
			else: retStr += unicode(tkn[0], incharset, 'ignore').encode(outcharset, 'ignore')
	except:
		printError("%-7s | Can't decodeCHARSET(%s, %s, %s)" % (szPROCESS, incharset, outcharset, key19), szPROCESS)
		try: 
			if incharset == outcharset: retStr = str
			else: retStr = unicode(str, incharset, 'ignore').encode(outcharset, 'ignore')
		except: 
			printError("%-7s | Can't decodeCHARSET.all(%s, %s, %s)" % (szPROCESS, incharset, outcharset, key19), szPROCESS)
			retStr = str
	return retStr

################################################################################
def convertIDN(str):
	if conf['IDN_USE'] == 1:
		# id@domain
		tkns = str.strip().lower().split('@')
		# Email ID part
		# Encode ('utf-8', 'ignore') - error handling -> ignore the character and continue with the next.
		# Decode in IDN_CHARSET (Unicode) -> Encode in UTF-8
		tkns[0] = tkns[0].strip('"').strip().decode(conf['IDN_CHARSET'], 'ignore').encode('utf-8', 'ignore');

		if len(tkns) > 1:
			# Domain part
			tkns[1] = tkns[1].strip('"').strip().decode(conf['IDN_CHARSET'], 'ignore').encode('utf-8', 'ignore')
			# If it is a punycode, decode it to Unicode and then encode in UTF-8 
			if tkns[1].startswith('xn--'):
				try: tkns[1] = tkns[1].decode('idna').encode('utf-8', 'ignore')
				except: printError("common  | Can't decode idna(%s)" % tkns[1], 'common')
			retStr = '%s@%s' % (tkns[0], tkns[1])
		else: retStr = tkns[0]
	else: retStr = str
	return retStr
################################################################################
def makeDIR():
	try:
		if not os.path.exists(conf['LOG_DIR']): os.makedirs(conf['LOG_DIR'], 0700)
		if not os.path.exists(conf['RECV_QUEUE']): os.makedirs(conf['RECV_QUEUE'], 0700)
		if not os.path.exists(conf['SEND_QUEUE']): os.makedirs(conf['SEND_QUEUE'], 0700)
		if not os.path.exists('%s/ing' % conf['SEND_QUEUE']): os.makedirs('%s/ing' % conf['SEND_QUEUE'], 0700)
		if not os.path.exists('%s/before' % conf['SEND_QUEUE']): os.makedirs('%s/before' % conf['SEND_QUEUE'], 0700)
		if not os.path.exists('%s/original' % conf['SEND_QUEUE']): os.makedirs('%s/original' % conf['SEND_QUEUE'], 0700)
		if not os.path.exists(conf['DECODE_QUEUE']): os.makedirs(conf['DECODE_QUEUE'], 0700)
		if not os.path.exists('%s/ing' % conf['DECODE_QUEUE']): os.makedirs('%s/ing' % conf['DECODE_QUEUE'], 0700)
		if not os.path.exists('%s/before' % conf['DECODE_QUEUE']): os.makedirs('%s/before' % conf['DECODE_QUEUE'], 0700)
		if not os.path.exists('%s/attach' % conf['DECODE_QUEUE']): os.makedirs('%s/attach' % conf['DECODE_QUEUE'], 0700)
		if not os.path.exists('%s/rfc822' % conf['DECODE_QUEUE']): os.makedirs('%s/rfc822' % conf['DECODE_QUEUE'], 0700)
		if not os.path.exists('%s/backup' % conf['DECODE_QUEUE']): os.makedirs('%s/backup' % conf['DECODE_QUEUE'], 0700)
		if not os.path.exists(conf['ENCODE_QUEUE']): os.makedirs(conf['ENCODE_QUEUE'], 0700)
		if not os.path.exists('%s/ing' % conf['ENCODE_QUEUE']): os.makedirs('%s/ing' % conf['ENCODE_QUEUE'], 0700)
		if not os.path.exists('%s/before' % conf['ENCODE_QUEUE']): os.makedirs('%s/before' % conf['ENCODE_QUEUE'], 0700)
		if not os.path.exists('%s/original' % conf['ENCODE_QUEUE']): os.makedirs('%s/original' % conf['ENCODE_QUEUE'], 0700)

		if os.getuid() != pwd.getpwnam(conf['USER'])[2]:
			if pwd.getpwnam(conf['USER'])[2] != os.stat('%s/conf/%s.conf' % (conf['BASE_PATH'], PROGRAM_NAME))[stat.ST_UID]:
				try: os.system('chown -f -R %s:%s %s' % (conf['USER'], conf['GROUP'], conf['BASE_PATH']))
				except: printError("common  | Can't chown %s:%s %s" % (conf['USER'], conf['GROUP'], conf['BASE_PATH']), 'common')
			if pwd.getpwnam(conf['USER'])[2] != os.stat(conf['LOG_DIR'])[stat.ST_UID]:
				try: os.system('chown -f -R %s:%s %s' % (conf['USER'], conf['GROUP'], conf['LOG_DIR']))
				except: printError("common  | Can't chown %s:%s %s" % (conf['USER'], conf['GROUP'], conf['LOG_DIR']), 'common')
			if pwd.getpwnam(conf['USER'])[2] != os.stat(conf['RECV_QUEUE'])[stat.ST_UID]:
				try: os.system('chown -f -R %s:%s %s' % (conf['USER'], conf['GROUP'], conf['RECV_QUEUE']))
				except: printError("common  | Can't chown %s:%s %s" % (conf['USER'], conf['GROUP'], conf['RECV_QUEUE']), 'common')
			if pwd.getpwnam(conf['USER'])[2] != os.stat(conf['SEND_QUEUE'])[stat.ST_UID]:
				try: os.system('chown -f -R %s:%s %s' % (conf['USER'], conf['GROUP'], conf['SEND_QUEUE']))
				except: printError("common  | Can't chown %s:%s %s" % (conf['USER'], conf['GROUP'], conf['SEND_QUEUE']), 'common')
			if pwd.getpwnam(conf['USER'])[2] != os.stat(conf['DECODE_QUEUE'])[stat.ST_UID]:
				try: os.system('chown -f -R %s:%s %s' % (conf['USER'], conf['GROUP'], conf['DECODE_QUEUE']))
				except: printError("common  | Can't chown %s:%s %s" % (conf['USER'], conf['GROUP'], conf['DECODE_QUEUE']), 'common')
			if pwd.getpwnam(conf['USER'])[2] != os.stat(conf['ENCODE_QUEUE'])[stat.ST_UID]:
				try: os.system('chown -f -R %s:%s %s' % (conf['USER'], conf['GROUP'], conf['ENCODE_QUEUE']))
				except: printError("common  | Can't chown %s:%s %s" % (conf['USER'], conf['GROUP'], conf['ENCODE_QUEUE']), 'common')
	except: printError("common  | Can't makeDIR()", 'common')

################################################################################
def makeEMAIL(email):

	# Return a copy of the email string with the leading and trailing characters removed and upper case letters converted to lower case.
	email = email.strip().lower()

	# If email string does not have @ character, return the "email@localhost" string
	if email.find('@') == -1: 
		return '%s@%s' % (email, conf['SERVER_NAME'])
	else: 
		return email

################################################################################
# Create a timestamp (19 digits)
################################################################################
def makeKEY19(second):

	t = datetime.datetime.now()

	# time.strftime() - Return a string representing the time, controlled by an explicit format string.
	# t2 - Send an email at given time
	# Send immediately	
	if second == 0: 
		t2 = time.strftime('%Y%m%d%H%M%S', time.localtime(time.time()))
	else: 
		# Reserved send
		t2 = time.strftime('%Y%m%d%H%M%S', time.localtime(second))

	szTemp = '%s%05x' % (t2, t.microsecond)
	return szTemp
	
################################################################################
def printLOG(str, szPROCESS):
	t = datetime.datetime.now()
	if szPROCESS == 'console':
		print('%04d/%02d/%02d %02d:%02d:%02d | %s\n' % (t.year, t.month, t.day, t.hour, t.minute, t.second, str))
	else:
		logfile = '%s/%s_%04d%02d%02d.log' % (conf['LOG_DIR'], szPROCESS, t.year, t.month, t.day)
		try:
			f = open(logfile, 'a')
			f.write('%04d/%02d/%02d %02d:%02d:%02d | %s\n' % (t.year, t.month, t.day, t.hour, t.minute, t.second, str))
			f.close()
		except: 
			print('%04d/%02d/%02d %02d:%02d:%02d | %s\n' % (t.year, t.month, t.day, t.hour, t.minute, t.second, str))
		else:
			try:
				if os.getuid() != pwd.getpwnam(conf['USER'])[2]: os.chown(logfile, pwd.getpwnam(conf['USER'])[2], grp.getgrnam(conf['GROUP'])[2])
			except: printError("common | Can't chown %s:%s %s" % (conf['USER'], conf['GROUP'], logfile), 'common')

################################################################################
def printError(szSTR, szPROCESS):
	printLOG(szSTR, 'error')
	info = sys.exc_info()
	for szFILE, szLINENO, szFN, szTEXT in traceback.extract_tb(info[2]): printLOG("%-7s | %s %s %s %s" % (szPROCESS, szFILE, szLINENO, szFN, szTEXT), 'error')

################################################################################
def loadConf(szPROCESS):

	file = '%s/conf/%s.conf' % (conf['BASE_PATH'], PROGRAM_NAME)

	# ST_MTIME - Time of last modification.
	try: 
		if os.stat(file)[stat.ST_MTIME] <= lmtime['CONF'][0] and lmtime['CONF'][1]+LOAD_INTERVAL > time.time(): return -1
		f = open(file, 'r')
	except:
		printError("%-7s | Can't open %s" % (szPROCESS, file), szPROCESS)
		printLOG("%-7s | Can't open %s" % (szPROCESS, file), 'console')
		os.sys.exit(-1)

	# Get cmdStr and valueStr from each line
	for lineStr in f:
		lineStr = lineStr.split('#')[0].strip()
		lineStr = lineStr.split(';')[0].strip()

		if lineStr == '': continue
		cmdStr = lineStr.split('=')[0].upper().strip()
		valueStr = lineStr[lineStr.find('=')+1:].strip()

		if cmdStr == '' or valueStr == '': continue

		if cmdStr == 'USER': 
			conf['USER'] = valueStr.lower()
			try: pwd.getpwnam(conf['USER'])
			except: 
				printError('common  | There is no %s user' % conf['USER'], 'common')
				printLOG('There is no %s user' % conf['USER'], 'console')
				os.sys.exit(-1)
		elif cmdStr == 'GROUP':
			conf['GROUP'] = valueStr.lower()
			try: grp.getgrnam(conf['GROUP'])
			except: 
				printError('common | There is no %s group' % conf['GROUP'], 'common')
				printLOG('There is no %s group' % conf['GROUP'], 'console')
				os.sys.exit(-1)
		elif cmdStr in ('ESMTPD_TYPE', 'POP3D_TYPE', 'SENDER_TYPE', 'DECODER_TYPE', 'ENCODER_TYPE'):
			conf[cmdStr] = valueStr.lower()
		elif cmdStr in ('SERVER_NAME', 'RETURN_ID', 'CHARSET', 'IDN_CHARSET'): conf[cmdStr] = valueStr.lower()
		elif cmdStr in ('ESMTPD_PORT', 'ESMTPD_TIMEOUT', 'ESMTPD_MAX_THREADS',
							'POP3D_PORT', 'POP3D_TIMEOUT', 'POP3D_MAX_THREADS',
							'SENDER_PORT', 'SENDER_TIMEOUT', 'SENDER_MAX_THREADS', 'SENDER_RETRY_COUNT', 'SENDER_RETRY_INTEVAL',
							'DECODER_MAX_THREADS',
							'ENCODER_MAX_THREADS', 'IDN_USE'): conf[cmdStr] = int(valueStr)
		elif cmdStr in ('LOG_DIR', 'RECV_QUEUE', 'SEND_QUEUE', 'DECODE_QUEUE', 'ENCODE_QUEUE'): conf[cmdStr] = valueStr
	f.close()

	lmtime['CONF'][0] = os.stat(file)[stat.ST_MTIME]
	lmtime['CONF'][1] = time.time()

	if szPROCESS != 'common': printLOG('%-7s | %s is loaded' % (szPROCESS, file), 'common')

	makeDIR()
	lmtime['VH'][1] = 0
	loadVH(szPROCESS)

################################################################################
# Virtual Host
################################################################################
def loadVH(szPROCESS):

	global vh
	temp = {}
	userCnt = 0

	file = '%s/conf/%s.vh' % (conf['BASE_PATH'], PROGRAM_NAME)

	try:
		# If there is a change in a configuration file, daemon will be reloaded automatically.
		# daemon will be reloaded automatically every 24 hours
		if os.stat(file)[stat.ST_MTIME] <= lmtime['VH'][0] and lmtime['VH'][1]+LOAD_INTERVAL > time.time(): return -1
		f = open(file, 'r')
	except:
		printError("%-7s | Can't load domainlist" % szPROCESS, szPROCESS)
		printLOG("%-7s | Can't load domainlist" % szPROCESS, 'console')
		os.sys.exit(-1)

	for lineStr in f:
		lineStr = lineStr.split('#')[0].strip()
		lineStr = lineStr.split(';')[0].strip()
		tkn = lineStr.split(',')

		if lineStr == '' or len(tkn) < 2: continue

		for szTemp in temp.values():
			if szTemp[0] == tkn[1].strip():
				temp[tkn[0].strip().lower()] = tkn[1].strip()
				break

		if temp.get(tkn[0].strip().lower(), '') != '': continue

		temp[tkn[0].strip().lower()] = tkn[1].strip()

		try:
			connDB = connectDB(tkn[1].strip())
			curDB = connDB.cursor()
		except: printLOG("%-7s | Can't connect to %s(%s)" % (szPROCESS, tkn[0].strip().lower(), tkn[1].strip()), 'error')
		else:
			try:
				curDB.execute('select count(*) from memberlist')
				row = curDB.fetchone()
				userCnt += int(row[0])
			except: printLOG("%-7s | Can't access memberlist TABLE %s" % (szPROCESS, tkn[0].strip().lower()), 'common')
			closeDB(connDB, curDB)
	f.close()

	vh = temp

	lmtime['VH'][0] = os.stat(file)[stat.ST_MTIME]
	lmtime['VH'][1] = time.time()

	if szPROCESS != 'common': printLOG('%-7s | %s is loaded' % (szPROCESS, file), 'common')
 
################################################################################
# /NIDAMail/conf/NIDAMail.access
################################################################################
def loadAccess(szPROCESS):

	global access
	temp = []

	file = "%s/conf/%s.access" % (conf['BASE_PATH'], PROGRAM_NAME)

	try:
		if os.stat(file)[stat.ST_MTIME] <= lmtime['ACCESS']: return -1
		f = open(file, 'r')
	except:
		printLOG("%-7s | Can't open %s" % (szPROCESS, file), 'error')
		printLOG("%-7s | Can't open %s" % (szPROCESS, file), 'console')
		os.sys.exit(-1)

	for lineStr in f:
		lineStr = lineStr.split('#')[0].strip()
		lineStr = lineStr.split(';')[0].strip()
		lineStr = lineStr.lower()
		tkn = lineStr.split()
		if lineStr == '' or len(tkn) < 3: continue
		temp.append(tkn)

	f.close()

	# Reverse the order of the items in the array
	temp.reverse()

	access = temp

	lmtime['ACCESS'] = os.stat(file)[stat.ST_MTIME]

	if szPROCESS != 'common': printLOG('%-7s | %s is loaded' % (szPROCESS, file), 'common')

################################################################################
def searchVH(szDOMAIN):
	szDOMAIN = szDOMAIN.strip().lower()
	return vh.get(szDOMAIN, '')

################################################################################
def search_access(str, direct):
	# Localhost is allowed for relaying -> Loop can be happended. (In case of virtual hosting configuration...)
	# Send an email to id@127.0.0.1 -> Which virtual domain will be received that email.?
	# 127.0.0.1 -> A.kr (/home/A/index.html) , B.kr (/home/B/index.html), C.kr (/home/C/index.html) (Virtual Host)
	############################################################################
	# richard@A.kr -> peter@B.kr (Alias)
	############################################################################
	# Additional check is required.
	############################################################################
	if str == PROGRAM_MAIL: 
		return ['', '', '']
	#-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
	for rdata in access:
		if re.search(rdata[0], str) and rdata[1] in direct: 
			return rdata
	#-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
	return ['', '', '']
################################################################################
def connectDB(szDSN):
	connDB = sqlite.connect(szDSN)
	return connDB

################################################################################
def closeDB(connDB, curDB):
	try:
		curDB.close()
		connDB.close()
	except: printLOG("common  | Can't close DB", 'error')

################################################################################
def searchUserPath(email):

	if email.strip() == '': return ['no domain', 'no domain', '']

	email = makeEMAIL(email)

	# Split ID and domain part based on @ character
	id = email.split('@')[0]
	domain = email.split('@')[1]

	if searchVH(domain) == '': return ['no domain', 'no domain', '']
	else:
		try:
			dsn = searchVH(domain)
			connDB = connectDB(dsn)
			curDB = connDB.cursor()
		except: 
			printLOG("common  | Can't connect to %s(%s)" % (domain, dsn), 'error')
			try:
				domain_path = dsn.replace(dsn.replace('\\', '/').split('/')[-1], '')
				if not os.path.exists(domain_path): shutil.copytree('%s/domain_v2' % conf['BASE_PATH'], domain_path)
			except: printLOG("common  | Can't copy %s/domain to %s" % (conf['BASE_PATH'], domain_path), 'error')
			else: printLOG('common  | %s is added' % dsn, 'common')
		else:
			i = 0
			while i <= 10:
				try:
					# Status check 
					curDB.execute("select path, name from memberlist where (eid='%s' or nid='%s') and status <= 2" % (id, id))
					row = curDB.fetchone()
					# Count a row
					curDB.execute("select count(*) from memberlist where (eid='%s' or nid='%s') and status <= 2" % (id, id))
					row2 = curDB.fetchone()
					# Check if size is less than 100%
					curDB.execute("select count(*) from memberlist where (eid='%s' or nid='%s') and status <= 2 and size < 100" % (id, id))
					row3 = curDB.fetchone()
				except:
					time.sleep(1)
					i += 1
				else: break
			if i > 10:
				closeDB(connDB, curDB)
				printLOG("common  | Can't searchUserPath(%s)" % email, 'error')
			else:
				closeDB(connDB, curDB)
				
				# if the SQL query result from Mbox DB is 0
				# if row2 is 0, it means there is no such a user.
				if int(row2[0]) == 0: 
					return ['no user', 'no user', '']
				elif not os.path.exists(row[0]):
					# Initilize - Copy default files in the /NIDAMail/mbox_v2 to rows[0]
					try: 
						shutil.copytree('%s/mbox_v2' % conf['BASE_PATH'], row[0])
					except:
						printLOG("common | Can't copy %s/mbox to %s" % (conf['BASE_PATH'], row[0]), 'error')
						return ['no user', 'no user', '']
					else:
						printLOG('common | %s(%s) is added' % (email, row[0]), 'common')
						if int(row3[0]) == 0: 
							return [row[0], 'size over', row[1]]
						else: 
							return [row[0], row[0], row[1]]
				# Size over
				elif int(row3[0]) == 0: return [row[0], 'size over', row[1]]
				else: return [row[0], row[0], row[1]]
		return ['no user', 'no user', '']
################################################################################
def get_nowsize(email):

	path = searchUserPath(email)[0]

	if path in ('no user', 'no domain'): return -1
	try:
		szDSN = '%s/.sqlite' %  path
		connDB = connectDB(szDSN)
		curDB = connDB.cursor()
	except: 
		printLOG("common | Can't connect to %s(%s)" % (email, szDSN), 'error')
		return -1
	else:
		try:
			curDB.execute('select ifnull(sum(size), 0) from maillist')
			row = curDB.fetchone()
			nowsize = int(row[0])
			curDB.execute('select ifnull(sum(size), 0) from filelist')
			row = curDB.fetchone()
			nowsize += int(row[0])
		except: 
			printLOG("common | Can't get_nowsize(%s)" % email, 'error')
			closeDB(connDB, curDB)
			return -1
		else: 
			closeDB(connDB, curDB)
			return nowsize

################################################################################
def update_nowsize(email, size):

	email = makeEMAIL(email)

	id = email.split('@')[0]
	domain = email.split('@')[1]

	if searchVH(domain) == '': return -1
	else:
		try:
			connDB = connectDB(searchVH(domain))
			curDB = connDB.cursor()
		except: 
			printLOG("common | Can't connect to %s(%s)" % (domain, searchVH(domain)), 'error')
			return -1
		else:
			try:
				curDB.execute("update memberlist set size=round(%d/(quota*1024*1024)*100) where eid='%s' or nid='%s'" % (size, id, id))
				connDB.commit()
			except: 
				closeDB(connDB, curDB)
				printLOG("common | Can't update_nowsize(%s, %d)" % (email, size), 'error')
				return -1
			else: return 1

################################################################################
def iscorrect_password(email, passwd):

	email = makeEMAIL(email)

	id = email.split('@')[0]
	domain = email.split('@')[1]

	if searchVH(domain) == '': return -1
	else: 
		try:
			connDB = connectDB(searchVH(domain))
			curDB = connDB.cursor()
		except: printLOG("common | Can't connect to %s(%s)" % (domain, searchVH(domain)), 'error')
		else:
			i = 0
			while i <= 10:
				try:
					curDB.execute("select passwd from memberlist where eid='%s' or nid='%s'" % (id, id))
					row = curDB.fetchone()
					# crypt.crypt(word, salt) - one-way hash function based upon a modified DES algorithm
					passwdCrypt = crypt.crypt(passwd, row[0])
					# REVISE CODE ##############################################
					curDB.execute("select count(*) from memberlist where (eid='%s' or nid='%s') and (passwd='%s' or passwd='%s')" % (id, id, passwd, passwdCrypt))
					# REVISE CODE ##############################################
					row2 = curDB.fetchone()
				except:
					time.sleep(1)
					i += 1
				else: break
			try:
				passwdCrypt = crypt.crypt(passwd, row[0])
				# update latest user login date
				# REVISE CODE ##################################################
				curDB.execute("update memberlist set logindate='%s' where (eid='%s' or nid='%s') and (passwd='%s' or passwd='%s')" % (makeKEY19(0), id, id, passwd, passwdCrypt))
				# REVISE CODE ##################################################
				connDB.commit()
			except: printLOG("common | Can't iscorrect_password(%s, %s) - update" % (email, passwd), 'error')

			if i > 10:
				printLOG("common | Can't iscorrect_password(%s, %s)" % (email, passwd), 'error')
				closeDB(connDB, curDB)
	  		else:
				closeDB(connDB, curDB)
				if int(row2[0]) == 1: return 1
		return -1

################################################################################
def setUIDGID(szPROCESS):
	try:
		os.setgid(grp.getgrnam(conf['GROUP'])[2])
		os.setuid(pwd.getpwnam(conf['USER'])[2])
	except:
		printLOG("%-7s | Can't setUIDGID(%s, %s)" % (szPROCESS, conf['USER'], conf['GROUP']), 'error')
		printLOG("%-7s | Can't setUIDGID(%s, %s)" % (szPROCESS, conf['USER'], conf['GROUP']), 'console')
		os.sys.exit(-1)

################################################################################
def waitPID():
	try: os.wait()
	except: pass

################################################################################
# Configuration
loadConf('common')
loadVH('common')
loadAccess('common')

################################################################################
# End of file
################################################################################
